package org.movee.net.cfg.parser.parser.cisco;

import org.movee.net.cfg.parser.antlr.IndentedRowCfgParser;
import org.movee.net.cfg.parser.domain.parser.IndentedRowConfigNode;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;

import java.util.List;

/**
 *
 *
 * @author 
 */
@Slf4j
public class CiscoCfgParseTreeWalker {

    private CiscoConfigNode currCfgNode = null;
    private final CiscoConfigContext context;
    private final CiscoSystemParser sysParser = new CiscoSystemParser();
    private final CiscoIntfParser intfParser = new CiscoIntfParser();
    private final CiscoIntfIpParser ipParser = new CiscoIntfIpParser();
    private final CiscoIntfIp6Parser ip6Parser = new CiscoIntfIp6Parser();
    private final CiscoBgpParser bgpParser = new CiscoBgpParser();
    private final CiscoBgpPeerParser bgpPeerParser = new CiscoBgpPeerParser();
    private final ObjectMapper jacksonMapper = new ObjectMapper();

    public CiscoCfgParseTreeWalker(String mgmtIp, String vendor) {
        this.context = new CiscoConfigContext(mgmtIp, vendor);
        this.currCfgNode = createRootConfigNode(context);
    }

    public CiscoConfigModel getModel() {
        return context.getModel();
    }

    public void walk(IndentedRowConfigNode node) {

        enterFile(node);

        List<IndentedRowConfigNode> children = node.getChildren();
        if (children != null) {
            for (IndentedRowConfigNode child : children) {
                internalWalk(child);
            }
        }

        exitFile(node);
    }

    /**
     * 深度优先遍历AST tree
     * @param node tree node
     */
    private void internalWalk(IndentedRowConfigNode node) {
        enterRow(node);

        List<IndentedRowConfigNode> children = node.getChildren();
        if (children != null) {
            for (IndentedRowConfigNode child : children) {
                internalWalk(child);
            }
        }

        exitRow(node);
    }

    public void enterFile(IndentedRowConfigNode node) {

    }

    public void exitFile(IndentedRowConfigNode node) {

    }

    public void enterRow(IndentedRowConfigNode node) {

        IndentedRowCfgParser.RowContext ctx = node.getCtx();

        CiscoConfigNode cfgNode = createConfigNode(currCfgNode, ctx);
        switch (cfgNode.getEntityType()) {
            case SYSTEM: {
                sysParser.parse(cfgNode, ctx);
                break;
            }
            case INTERFACE: {
                intfParser.parse(cfgNode, ctx);
                break;
            }
            case INTERFACE_IP: {
                ipParser.parse(cfgNode, ctx);
                break;
            }
            case INTERFACE_IP6: {
                ip6Parser.parse(cfgNode, ctx);
                break;
            }
            case BGP: {
                bgpParser.parse(cfgNode, ctx);
                break;
            }
            case BGP_PEER: {
                bgpPeerParser.parse(cfgNode, ctx);
                break;
            }
            default: {
                //
            }
        }

        currCfgNode = cfgNode;

    }

    public void exitRow(IndentedRowConfigNode node) {
        if (currCfgNode.getIsEntityRoot()) {
            CiscoConfigContext context = currCfgNode.getContext();
            CiscoConfigModel model = context.getModel();
            CiscoConfigEntityType type = currCfgNode.getEntityType();

            Object entityInstance = context.remParsingEntity(type);
            switch (type) {
                case INTERFACE: {
                    CiscoConfigModel.Interface intf = (CiscoConfigModel.Interface) entityInstance;
                    model.putIntf(intf.getName(), intf);
                    break;
                }
                case INTERFACE_IP: {
                    CiscoConfigModel.Interface intf = (CiscoConfigModel.Interface) context.getParsingEntity(CiscoConfigEntityType.INTERFACE);
                    intf.getIps().add((CiscoConfigModel.InterfaceIp) entityInstance);
                    break;
                }
                case INTERFACE_IP6: {
                    CiscoConfigModel.Interface intf = (CiscoConfigModel.Interface) context.getParsingEntity(CiscoConfigEntityType.INTERFACE);
                    intf.getIp6s().add((CiscoConfigModel.InterfaceIp6) entityInstance);
                    break;
                }
                case BGP: {
                    CiscoConfigModel.Bgp bgp = (CiscoConfigModel.Bgp) entityInstance;
                    model.setBgp(bgp);
                    break;
                }
                case BGP_PEER: {
                    CiscoConfigModel.Bgp bgp = (CiscoConfigModel.Bgp) context.getParsingEntity(CiscoConfigEntityType.BGP);
                    CiscoConfigModel.BgpPeer bgpPeer = (CiscoConfigModel.BgpPeer) entityInstance;
                    bgp.getPeers().put(bgpPeer.getPeerIp(), bgpPeer);
                    break;
                }
                default: {
                    //
                }
            }
        }

        currCfgNode = currCfgNode.getParent();
    }

    private CiscoConfigNode createConfigNode(CiscoConfigNode parent, IndentedRowCfgParser.RowContext ctx) {

        String word0 = ctx.WORD(0) == null ? "" : ctx.WORD(0).getText();
        String word1 = ctx.WORD(1) == null ? "" : ctx.WORD(1).getText();

        CiscoConfigNode cfgNode = new CiscoConfigNode();
        cfgNode.setParent(parent);
        cfgNode.setWord0(word0);
        cfgNode.setWord1(word1);
        cfgNode.setIsRoot(false);
        cfgNode.setContext(parent.getContext());

        log.info("word0: {}, word1: {}", word0, word1);
        if ("hostname".equals(word0) && parent.getIsRoot()) {
            cfgNode.setIsEntityRoot(true);
            cfgNode.setEntityType(CiscoConfigEntityType.SYSTEM);
        } else if ("version".equals(word0) && parent.getIsRoot()) {
            cfgNode.setIsEntityRoot(true);
            cfgNode.setEntityType(CiscoConfigEntityType.SYSTEM);
        } else if ("interface".equals(word0) && parent.getIsRoot()) {
            log.debug("{}", ctx.getText());
            cfgNode.setIsEntityRoot(true);
            cfgNode.setEntityType(CiscoConfigEntityType.INTERFACE);
        } else if ("router".equals(word0) && "bgp".equals(word1) && parent.getIsRoot()) {
            cfgNode.setIsEntityRoot(true);
            cfgNode.setEntityType(CiscoConfigEntityType.BGP);
        } else if ("neighbor".equals(word0)
                && parent.getIsEntityRoot()
                && parent.getEntityType() == CiscoConfigEntityType.BGP) {
            cfgNode.setIsEntityRoot(true);
            cfgNode.setEntityType(CiscoConfigEntityType.BGP_PEER);
        } else if ("ip".equals(word0) && "address".equals(word1)
                && parent.getIsEntityRoot()
                && parent.getEntityType() == CiscoConfigEntityType.INTERFACE) {
            cfgNode.setIsEntityRoot(true);
            cfgNode.setEntityType(CiscoConfigEntityType.INTERFACE_IP);
        } else if ("ipv6".equals(word0) && "address".equals(word1)
                && parent.getIsEntityRoot()
                && parent.getEntityType() == CiscoConfigEntityType.INTERFACE) {
            cfgNode.setIsEntityRoot(true);
            cfgNode.setEntityType(CiscoConfigEntityType.INTERFACE_IP6);
        } else if (parent.getIsRoot()) {
            // 暂不支持解析的一级节点
            cfgNode.setIsEntityRoot(true);
            cfgNode.setEntityType(CiscoConfigEntityType.UNKNOWN);
        } else {
            // 支持解析或不支持解析的非根节点
            cfgNode.setIsEntityRoot(false);
            // 继承父节点的
            cfgNode.setEntityType(parent.getEntityType());
        }

        return cfgNode;
    }


    private CiscoConfigNode createRootConfigNode(CiscoConfigContext context) {
        CiscoConfigNode cfgNode = new CiscoConfigNode();
        cfgNode.setParent(null);
        cfgNode.setWord0("root");
        cfgNode.setWord1("root");
        cfgNode.setIsRoot(true);
        cfgNode.setIsEntityRoot(false);
        cfgNode.setEntityType(CiscoConfigEntityType.ROOT);
        cfgNode.setContext(context);
        return cfgNode;
    }


}
